#include <auto_json.h>
#include <cJSON.h>
#include <string>
#include <esp_log.h>

static const char *TAG = "Auto_Json";
#define Debug 0

AutoJson::AutoJson(json_type_t type)
{
    if (type == json_type_t::ARRAY_JSON)
        root = cJSON_CreateArray();
    else
        root = cJSON_CreateObject();
}
AutoJson ::AutoJson(cJSON *const root, cJSON *const father, const std::string &self_key, json_type_t father_type)
{
    this->root = root;
    this->father = father;
    is_this_obj_son = true;
    this->father_type = father_type;
    this->self_key = self_key;
}
AutoJson ::AutoJson(cJSON *const root, cJSON *const father, uint8_t self_index_in_father_array, json_type_t father_type)
{
    this->root = root;
    this->father = father;
    is_this_obj_son = true;
    this->father_type = father_type;
    this->self_index_in_father_array = self_index_in_father_array;
}
AutoJson::AutoJson(const std::string &json)
{
    root = cJSON_Parse(json.c_str());
    if (!root)
        ESP_LOGE(TAG, "parse %s failed at %s %d", json.c_str(), __FUNCTION__, __LINE__);
}
AutoJson::AutoJson(const char *json)
{
    root = cJSON_Parse(json);
    if (!root)
        ESP_LOGE(TAG, "parse %s failed at %s %d", json, __FUNCTION__, __LINE__);
}

AutoJson::~AutoJson()
{
    if (!is_this_obj_son && root)
        cJSON_Delete(root);

#if Debug
    if (is_this_obj_son)
        ESP_LOGW(TAG, "This is a son object will not delete!");
    else
        ESP_LOGW(TAG, "JSON deleted!");
#endif
}

int AutoJson::asInt()
{
    return (int)asDouble();
}
std::string AutoJson::asString()
{
    if (!root)
        return "";
    char *result = cJSON_GetStringValue(root);

    if (result)
        return std::string(result);

    return "";
}
bool AutoJson::asBool()
{
    if (!root)
        return false;
    if (!cJSON_IsBool(root))
        return false;

    return cJSON_IsTrue(root);
}

double AutoJson::asDouble()
{
    if (!root)
        return 0;
    return cJSON_GetNumberValue(root);
}

AutoJson AutoJson::operator[](const std::string &key)
{
    if (!root)
    {
        ESP_LOGE(TAG, "root is null at %s %d", __FUNCTION__, __LINE__);
        return AutoJson(nullptr, nullptr, "", json_type_t::NULL_JSON);
    }

    cJSON *result = cJSON_GetObjectItem(root, key.c_str());
    if (result == nullptr) // not found the key
    {
        cJSON_AddItemToObject(root, key.c_str(), cJSON_CreateObject());
    }
    return AutoJson(cJSON_GetObjectItem(root, key.c_str()), root, key, json_type_t::OBJ_JSON);
}
AutoJson AutoJson::operator[](uint8_t index)
{
    if (!root || !cJSON_IsArray(root))
    {
        ESP_LOGE(TAG, "root is null or not array at %s %d", __FUNCTION__, __LINE__);
        return AutoJson(nullptr, nullptr, "", json_type_t::NULL_JSON);
    }

    cJSON *result = cJSON_GetArrayItem(root, index);
    if (result == nullptr) // not found the index in array
    {
        cJSON_AddItemToArray(root, cJSON_CreateObject());
    }

    return AutoJson(cJSON_GetArrayItem(root, index), root, index, json_type_t::ARRAY_JSON);
}

std::string AutoJson::print()
{
    std::string ret = "";
    char *json = cJSON_Print(root);

    if (!json)
    {
        ESP_LOGE(TAG, "print json failed at %s %d", __FUNCTION__, __LINE__);
        return ret;
    }

    ret = std::string(json);

    cJSON_free(json);

    return ret;
}
void AutoJson::del(const std::string &key)
{
    if (!root)
        return;
    cJSON_DeleteItemFromObject(root, key.c_str());
}
void AutoJson::del(uint8_t index)
{
    if (!root || !cJSON_IsArray(root))
        return;
    cJSON_DeleteItemFromArray(root, index);
}
AutoJson AutoJson::addArray(const std::string &key)
{
    if (!root)
        return AutoJson(nullptr, nullptr, "", json_type_t::NULL_JSON);

    json_type_t type = json_type_t::OBJ_JSON;
    if (cJSON_IsArray(root))
        type = json_type_t::ARRAY_JSON;

    cJSON *array = cJSON_AddArrayToObject(root, key.c_str());
    return AutoJson(array, root, key, type);
}
// void AutoJson::addItem(const std::string &key, const char *value)
// {
//     if (!root)
//         return;
//     cJSON_AddStringToObject(root, key.c_str(), value);
// }

// void AutoJson::addItem(const std::string &key, const std::string &value)
// {
//     return addItem(key, value.c_str());
// }
// void AutoJson::addItem(const std::string &key, int value)
// {
//     if (!root)
//         return;
//     cJSON_AddNumberToObject(root, key.c_str(), value);
// }
// void AutoJson::addItem(const std::string &key, bool value)
// {
//     if (!root)
//         return;
//     cJSON_AddBoolToObject(root, key.c_str(), value);
// }
// void AutoJson::addItem(const std::string &key, double value)
// {
//     if (!root)
//         return;
//     cJSON_AddNumberToObject(root, key.c_str(), value);
// }

// void AutoJson::addElement(const char *value)
// {
//     if (!root || !cJSON_IsArray(root))
//     {
//         ESP_LOGE(TAG, "root is null or not array at %s %d", __FUNCTION__, __LINE__);
//         return;
//     }
//     cJSON_AddItemToArray(root, cJSON_CreateString(value));
// }
// void AutoJson::addElement(const std::string &value)
// {
//     return addElement(value.c_str());
// }

// void AutoJson::addElement(int value)
// {
//     return addElement((double)value);
// }
// void AutoJson::addElement(bool value)
// {
//     return addElement((double)value);
// }
// void AutoJson::addElement(double value)
// {
//     if (!root || !cJSON_IsArray(root))
//     {
//         ESP_LOGE(TAG, "root is null or not array at %s %d", __FUNCTION__, __LINE__);
//         return;
//     }
//     cJSON_AddItemToArray(root, cJSON_CreateNumber(value));
// }
// void AutoJson::addElement(const std::string &key, const char *value)
// {
//     if (!root || !cJSON_IsArray(root))
//     {
//         ESP_LOGE(TAG, "root is null or not array at %s %d", __FUNCTION__, __LINE__);
//         return;
//     }
//     cJSON *item = cJSON_CreateObject();
//     cJSON_AddStringToObject(item, key.c_str(), value);
//     cJSON_AddItemToArray(root, item);
// }
// void AutoJson::addElement(const std::string &key, const std::string &value)
// {
//     return addElement(key, value.c_str());
// }
// void AutoJson::addElement(const std::string &key, int value)
// {
//     return addElement(key, (double)value);
// }
// void AutoJson::addElement(const std::string &key, bool value)
// {
//     return addElement(key, (double)value);
// }
// void AutoJson::addElement(const std::string &key, double value)
// {
//     if (!root || !cJSON_IsArray(root))
//     {
//         ESP_LOGE(TAG, "root is null or not array at %s %d", __FUNCTION__, __LINE__);
//         return;
//     }
//     cJSON *item = cJSON_CreateObject();
//     cJSON_AddNumberToObject(item, key.c_str(), value);
//     cJSON_AddItemToArray(root, item);
// }

// void AutoJson::setItem(const std::string &key, const char *value)
// {
//     return setItem(key, std::string(value));
// }

// void AutoJson::setItem(const std::string &key, const std::string &value)
// {
//     del(key);
//     addItem(key, value);
// }

// void AutoJson::setItem(const std::string &key, int value)
// {
//     del(key);
//     addItem(key, value);
// }
// void AutoJson::setItem(const std::string &key, bool value)
// {
//     del(key);
//     addItem(key, value);
// }
// void AutoJson::setItem(const std::string &key, double value)
// {
//     del(key);
//     addItem(key, value);
// }
void AutoJson::setVal(const char *value)
{
    if (!root)
        return;

    if (!cJSON_IsString(root))
    {
        if (father_type == json_type_t::ARRAY_JSON)
        {
            cJSON_ReplaceItemInArray(father, self_index_in_father_array, cJSON_CreateString(value));
            root = cJSON_GetArrayItem(father, self_index_in_father_array);
        }
        else if (father_type == json_type_t::OBJ_JSON)
        {
            cJSON_ReplaceItemInObject(father, self_key.c_str(), cJSON_CreateString(value));
            root = cJSON_GetObjectItem(father, self_key.c_str());
        }
        else
            return;
    }

    cJSON_SetValuestring(root, value);
}
void AutoJson::setVal(const std::string &value)
{
    return setVal(value.c_str());
}

void AutoJson::setVal(double value)
{
    if (!root)
        return;

    if (!cJSON_IsNumber(root) && is_this_obj_son)
    {
        if (father_type == json_type_t::ARRAY_JSON)
        {
            cJSON_ReplaceItemInArray(father, self_index_in_father_array, cJSON_CreateNumber(value));
            root = cJSON_GetArrayItem(father, self_index_in_father_array);
        }
        else if (father_type == json_type_t::OBJ_JSON)
        {
            cJSON_ReplaceItemInObject(father, self_key.c_str(), cJSON_CreateNumber(value));
            root = cJSON_GetObjectItem(father, self_key.c_str());
        }
        else
            return;
    }
    cJSON_SetNumberValue(root, value);
}
void AutoJson::setVal(bool value)
{
    if (!root)
        return;

    if (!cJSON_IsBool(root) && is_this_obj_son)
    {
        if (father_type == json_type_t::ARRAY_JSON)
        {
            cJSON_ReplaceItemInArray(father, self_index_in_father_array, cJSON_CreateBool(value));
            root = cJSON_GetArrayItem(father, self_index_in_father_array);
        }
        else if (father_type == json_type_t::OBJ_JSON)
        {
            cJSON_ReplaceItemInObject(father, self_key.c_str(), cJSON_CreateBool(value));
            root = cJSON_GetObjectItem(father, self_key.c_str());
        }
        else
            return;
    }
    cJSON_SetBoolValue(root, value);
}
void AutoJson::setVal(int value)
{
    return setVal((double)value);
}

void AutoJson::operator=(const char *value)
{
    setVal(value);
}
void AutoJson::operator=(const std::string &value)
{
    setVal(value);
}
void AutoJson::operator=(int value)
{
    setVal(value);
}
void AutoJson::operator=(bool value)
{
    setVal(value);
}
void AutoJson::operator=(double value)
{
    setVal(value);
}
